package com.acme.chat.client;

import static com.acme.chat.settings.Settings.LOCALE_BUNDLE;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

import com.acme.chat.message.ChatMessage;
import com.acme.chat.message.Message;
import com.acme.chat.message.MessageType;

public class ClientGUI extends JFrame implements ActionListener {

    private static final int SIZE_BORDER = 5;
    private static final int SIZE_TEXT_H = 20;
    private static final int COLUMS = 10;
    private static final int LEFT_X = 10;
    private static final int SIZE_BUTTON_H = 23;
    private static final int SIZE_BUTTON_W = 126;
    private static final int SIZE_LABEL_H = 14;
    private static final int SIZE_LABEL_W_SMALL = 46;
    private static final int SIZE_TEXT_FIELD_W = 86;
    private static final int SIZE_NAME_LABEL_W = 300;

    private static final int WIN_X = 100;
    private static final int WIN_Y = 100;
    private static final int SIZE_WINDOW_H = 591;
    private static final int SIZE_WINDOW_W = 494;

    private static final String WINDOW_NAME = LOCALE_BUNDLE.getString("WINDOW_NAME");
    private static final String LABEL_SERVER = LOCALE_BUNDLE.getString("LABEL_SERVER");
    private static final String DEFAULT_HOST = LOCALE_BUNDLE.getString("DEFAULT_HOST");
    private static final String LABEL_PORT = LOCALE_BUNDLE.getString("LABEL_PORT");
    private static final int DEFAULT_PORT = 6800;
    private static final String LABEL_LOGIN = LOCALE_BUNDLE.getString("LABEL_LOGIN");
    private static final String LABEL_LOGOUT = LOCALE_BUNDLE.getString("LABEL_LOGOUT");
    private static final String LABEL_LIST = LOCALE_BUNDLE.getString("LABEL_LIST");
    private static final String LABEL_HISTORY = LOCALE_BUNDLE.getString("LABEL_HISTORY");
    private static final String LABEL_CLEAR_LOG = LOCALE_BUNDLE.getString("LABEL_CLEAR_LOG");
    private static final String LABEL_ROOM = LOCALE_BUNDLE.getString("LABEL_ROOM");
    private static final String DEFAULT_ROOM = "1";
    private static final String LABEL_PRIVATE_NAME = LOCALE_BUNDLE.getString("LABEL_PRIVATE_NAME");
    private static final String ENTER_YOUR_NAME = LOCALE_BUNDLE.getString("ENTER_YOUR_NAME");
    private static final String ERROR_EMPTY_NAME = LOCALE_BUNDLE.getString("ERROR_EMPTY_NAME");
    private static final String ERROR_EMPTY_HOSTNAME = LOCALE_BUNDLE.getString("ERROR_EMPTY_HOSTNAME");
    private static final String ERROR_EMPTY_PORT = LOCALE_BUNDLE.getString("ERROR_EMPTY_PORT");
    private static final String ERROR_PORT_FORMAT = LOCALE_BUNDLE.getString("ERROR_PORT_FORMAT");
    private static final String ERROR_CANNOT_CONNECT = LOCALE_BUNDLE.getString("ERROR_CANNOT_CONNECT");
    private static final String ENTER_YOUR_MESSAGE = LOCALE_BUNDLE.getString("ENTER_YOUR_MESSAGE");
    private static final String MESSAGE_DISCONNECTED = "You are disconnected from the server!\n";
    private JLabel label;
    private JTextField messageField;
    private JTextField tfServer;
    private JTextField tfPort;
    private JTextField tfRoom;
    private JTextField tfPrivateName;
    private JButton login;
    private JButton logout;
    private JButton list;
    private JButton history;
    private JTextArea logTextArea;
    private boolean connected;
    private Client client;

    /**
     * Creates chat window.
     *
     * @param host
     * @param port
     */
    public ClientGUI() {
        super(WINDOW_NAME);

        this.setResizable(false);
        JPanel contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(SIZE_BORDER, SIZE_BORDER, SIZE_BORDER, SIZE_BORDER));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        tfServer = new JTextField();
        tfServer.setText(DEFAULT_HOST);
        tfServer.setBounds(50, 11, 86, SIZE_TEXT_H);
        contentPane.add(tfServer);
        tfServer.setColumns(COLUMS);

        JLabel lServer = new JLabel(LABEL_SERVER);
        lServer.setBounds(LEFT_X, 14, SIZE_LABEL_W_SMALL, SIZE_LABEL_H);
        contentPane.add(lServer);

        JLabel lPort = new JLabel(LABEL_PORT);
        lPort.setBounds(LEFT_X, 49, SIZE_LABEL_W_SMALL, SIZE_LABEL_H);
        contentPane.add(lPort);

        tfPort = new JTextField();
        tfPort.setText(String.valueOf(DEFAULT_PORT));
        tfPort.setBounds(50, 46, SIZE_TEXT_FIELD_W, SIZE_TEXT_H);
        contentPane.add(tfPort);
        tfPort.setColumns(COLUMS);

        JLabel lRoom = new JLabel(LABEL_ROOM);
        lRoom.setBounds(LEFT_X, 89, SIZE_LABEL_W_SMALL, SIZE_LABEL_H);
        contentPane.add(lRoom);

        tfRoom = new JTextField();
        tfRoom.setText(DEFAULT_ROOM);
        tfRoom.setBounds(50, 86, SIZE_TEXT_FIELD_W, SIZE_TEXT_H);
        contentPane.add(tfRoom);
        tfRoom.setColumns(COLUMS);

        messageField = new JTextField();
        messageField.setBounds(148, 46, 425, SIZE_TEXT_H);
        contentPane.add(messageField);
        messageField.setColumns(10);

        label = new JLabel(ENTER_YOUR_NAME);
        label.setFont(new Font("Tahoma", Font.PLAIN, 16));
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setBounds(192, 14, SIZE_NAME_LABEL_W, SIZE_LABEL_H);
        contentPane.add(label);

        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(148, 78, 425, 368);
        contentPane.add(scrollPane);

        logTextArea = new JTextArea();
        logTextArea.setLineWrap(true);
        logTextArea.setEditable(false);
        scrollPane.setViewportView(logTextArea);

        JLabel lPrivateName = new JLabel(LABEL_PRIVATE_NAME);
        lPrivateName.setBounds(LEFT_X, 138, SIZE_TEXT_FIELD_W, SIZE_LABEL_H);
        contentPane.add(lPrivateName);

        tfPrivateName = new JTextField();
        tfPrivateName.setBounds(LEFT_X, 164, SIZE_BUTTON_W, SIZE_TEXT_H);
        contentPane.add(tfPrivateName);
        tfPrivateName.setColumns(COLUMS);

        login = new JButton(LABEL_LOGIN);
        login.addActionListener(this);
        login.setBounds(LEFT_X, 387, SIZE_BUTTON_W, SIZE_BUTTON_H);
        contentPane.add(login);

        logout = new JButton(LABEL_LOGOUT);
        logout.addActionListener(this);
        logout.setBounds(LEFT_X, 423, SIZE_BUTTON_W, SIZE_BUTTON_H);
        logout.setEnabled(false);
        contentPane.add(logout);

        list = new JButton(LABEL_LIST);
        list.addActionListener(this);
        list.setBounds(LEFT_X, 233, SIZE_BUTTON_W, SIZE_BUTTON_H);
        list.setEnabled(false);
        contentPane.add(list);

        history = new JButton(LABEL_HISTORY);
        history.addActionListener(this);
        history.setBounds(LEFT_X, 197, SIZE_BUTTON_W, SIZE_BUTTON_H);
        history.setEnabled(false);
        contentPane.add(history);

        JButton clearLog = new JButton(LABEL_CLEAR_LOG);
        clearLog.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                clearLog();
            }
        });
        clearLog.setBounds(LEFT_X, 269, SIZE_BUTTON_W, SIZE_BUTTON_H);
        contentPane.add(clearLog);

        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                exit();
            }
        });
        setBounds(WIN_X, WIN_Y, SIZE_WINDOW_H, SIZE_WINDOW_W);

        setVisible(true);
    }

    private void exit() {
        if (client != null) {
            client.disconnect();
        }
        System.exit(0);
    }

    private void clearLog() {
        logTextArea.setText(null);
        logTextArea.setCaretPosition(0);
    }

    public void append(String str) {
        logTextArea.append(str);
        logTextArea.setCaretPosition(logTextArea.getText().length() - 1);
    }

    private Message processCommand(String text) {
        String[] commands = text.split("\\s+");
        if ((commands.length > 2) && ("/m".equals(commands[0]) || "/msg".equals(commands[0]))) {
            String nameTo = commands[1];
            StringBuilder sb = new StringBuilder();
            for (int i = 2; i < commands.length; i++) {
                sb.append(commands[i]).append(" ");
            }
            return new ChatMessage(sb.toString(), nameTo);
        }

        return null;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Object o = e.getSource();
        if (o.equals(logout)) {
            append(MESSAGE_DISCONNECTED);
            client.disconnect();
            return;
        }

        if (o.equals(list)) {
            client.sendMessage(new ChatMessage(MessageType.WHOISIN));
            return;
        }

        if (o.equals(history)) {
            client.sendMessage(new ChatMessage(MessageType.GET_HISTORY));
            return;
        }

        if (connected) {
            enterKeyAction();
            return;
        }

        if (o.equals(login)) {
            loginAction();
        }
    }

    private void enterKeyAction() {
        String room = tfRoom.getText().trim();

        if (client.changeRoom(room)) {
            client.sendMessage(new ChatMessage(client.getRoom()));
        }

        Message message = processCommand(messageField.getText());

        if (message != null) {
            client.sendMessage(message);
            messageField.setText("");
            return;
        }

        if (tfPrivateName.getText().length() != 0) {
            client.sendMessage(new ChatMessage(messageField.getText(), tfPrivateName.getText()));
            messageField.setText("");
            tfPrivateName.setText("");
        } else {
            client.sendMessage(new ChatMessage(MessageType.MESSAGE, messageField.getText()));
        }
        messageField.setText("");
    }

    private void loginAction() {
        String username = messageField.getText().trim();
        String room = tfRoom.getText().trim();
        if (username.length() == 0) {
            append(ERROR_EMPTY_NAME);
            return;
        }

        String server = tfServer.getText().trim();
        if (server.length() == 0) {
            append(ERROR_EMPTY_HOSTNAME);
            return;
        }

        String portNumber = tfPort.getText().trim();
        if (portNumber.length() == 0) {
            append(ERROR_EMPTY_PORT);
            return;
        }

        int port = 0;
        try {
            port = Integer.valueOf(portNumber);
        } catch (NumberFormatException nfe) {
            append(ERROR_PORT_FORMAT);
            return;
        }

        client = new Client(server, port, room, username, this);
        if (!client.start()) {
            append(ERROR_CANNOT_CONNECT);
            return;
        }

        onConnectActions();
    }

    private void onConnectActions() {
        connected = true;
        messageField.setText("");
        label.setText(ENTER_YOUR_MESSAGE + client.getName());
        login.setEnabled(false);
        logout.setEnabled(true);
        list.setEnabled(true);
        history.setEnabled(true);
        tfServer.setEditable(false);
        tfPort.setEditable(false);
        messageField.addActionListener(this);
    }

    public void onDisconnectActions() {
        connected = false;
        messageField.removeActionListener(this);
        login.setEnabled(true);
        logout.setEnabled(false);
        list.setEnabled(false);
        label.setText(ENTER_YOUR_NAME);
        messageField.setText(client.getName());
        tfServer.setEditable(false);
        tfPort.setEditable(false);
    }

    public static void main(String[] args) {
        new ClientGUI();
    }
}